import Long from "long";

export namespace cmm {
    export class Tuple2<T1, T2> {
        t1: T1
        t2: T2

        constructor(t1: T1, t2: T2) {
            this.t1 = t1;
            this.t2 = t2;
        }
    }

    export function map<T, U>(origin: T[], exc: (item: T) => U): U[] {
        let result: U[] = []
        for (let t of origin) {
            result.push(exc(t))
        }
        return result
    }

    export function array2Map<T, K, V>(origin: T[], exc: (item: T) => { k: K, v: V }): Map<K, V> {
        let result: Map<K, V> = new Map()
        for (let t of origin) {
            const ne = exc(t)
            result.set(ne.k, ne.v)
        }
        return result
    }

    export function mapValues<T, K>(origin: T[], exc: (item: T) => K): Map<K, T> {
        let result: Map<K, T> = new Map()
        for (let t of origin) {
            result.set(exc(t), t)
        }
        return result
    }

    export function map2array<T, K, V>(origin: Map<K, V>, exc: (key: K, value: V) => T): T[] {
        let result: T[] = []
        origin.forEach((v, k) => {
            result.push(exc(k, v))
        })
        return result
    }

    export function map2kv<K, V, K2 extends string | number | symbol, V2>(origin: Map<K, V>, exc: (k1: K, v1: V) => {
        k2: K2,
        v2: V2
    }): { [k3 in K2]: V2 } {
        let result: { [k in K2]: V2 } = {} as { [k in K2]: V2 }
        origin.forEach((v, k) => {
            const exc1 = exc(k, v);
            result[exc1.k2] = exc1.v2
        })
        return result
    }


    export function longMapMerge<T>(m1: Map<Long, T>, m2: Map<Long, T>, exc: (v1: T, v2: T) => T): Map<Long, T> {
        const kc = (key: Long, _: T) => key.toString()
        const m1t = cmm.entriesKeys(m1, kc)
        const m2t = cmm.entriesKeys(m2, kc)
        const result = new Map(m1t);
        m2t.forEach((v, k) => result.set(k, result.has(k) ? exc(v, result.get(k)!) : v));
        return cmm.entriesKeys(result, (key, _) => Long.fromString(key))
    }

    export function entriesValues<K, V, U>(origin: Map<K, V>, exc: (key: K, value: V) => U): Map<K, U> {
        let result: Map<K, U> = new Map()
        origin.forEach((v, k) => {
            result.set(k, exc(k, v))
        })
        return result
    }


    export function entriesKeys<K, V, K2>(origin: Map<K, V>, exc: (key: K, value: V) => K2): Map<K2, V> {
        let result: Map<K2, V> = new Map()
        origin.forEach((v, k) => {
            result.set(exc(k, v), v)
        })
        return result
    }

    export function entries<K1, V1, K2, V2>(origin: Map<K1, V1>, exc: (key: K1, value: V1) => {
        k2: K2,
        v2: V2
    }): Map<K2, V2> {
        let result: Map<K2, V2> = new Map()
        origin.forEach((v, k) => {
            const ne = exc(k, v)
            result.set(ne.k2, ne.v2)
        })
        return result
    }

}